package com.example.nioprc.infrastructure.proxy;

import com.example.nioprc.infrastructure.client.RpcClient;
import com.example.nioprc.infrastructure.protocol.RpcRequest;
import com.example.nioprc.infrastructure.protocol.RpcResponse;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.UUID;

/**
 * RPC代理工厂，用于创建服务接口的代理对象
 * 当客户端调用远程方法时，会调用代理对象的方法，代理对象会远程调用服务端的方法
 * 
 */
public class RpcProxyFactory {
    private final RpcClient rpcClient;
    public RpcProxyFactory(RpcClient rpcClient){
        this.rpcClient=rpcClient;
    }

    /**
     * 创建服务接口的代理对象
     * @param serviceInterface 服务接口类
     * @return 代理对象
     * @param <T> 泛型类型
     */
    public <T> T createProxy(Class<T> serviceInterface){
        return (T) Proxy.newProxyInstance(
                serviceInterface.getClassLoader(),
                new Class<?>[]{serviceInterface},
                new RpcInvocationHandler(rpcClient,serviceInterface.getName())
        );
    }

    /**
     * RPC调用处理器，实现Java动态代理的InvocationHandler接口，远程调用服务端的方法
     */
    private static class RpcInvocationHandler implements InvocationHandler{
        private final RpcClient rpcClient;
        private final String serviceName;
        public RpcInvocationHandler(RpcClient rpcClient,String serviceName){
            this.rpcClient=rpcClient;
            this.serviceName=serviceName;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            RpcRequest request = new RpcRequest();
            request.setRequestId(UUID.randomUUID().toString());
            request.setMethodName(method.getName());
            request.setParameterTypes(method.getParameterTypes());
            request.setParameters(args);
            request.setServiceName(serviceName);
            //发送RPC请求并获取响应
            RpcResponse response = rpcClient.sendRequest(request);
            if(response.getError()!=null){
                throw response.getError();
            }
            return response.getResult();
        }
    }
}
